package com.lej.dataservice.club.http.interceptor.logging

import okhttp3.*
import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.internal.platform.Platform
import java.io.IOException
import java.util.concurrent.TimeUnit

/**
 * @author ihsan on 09/02/2017.
 */
class LoggingInterceptor private constructor(private val builder: Builder) : Interceptor {
    private val isDebug: Boolean = builder.isDebug

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        if (builder.headers.size > 0) {
            val headers = request.headers
            val names = headers.names()
            val requestBuilder = request.newBuilder()
            requestBuilder.headers(builder.headers)
            for (name in names) {
                val value = headers[name]
                requestBuilder.addHeader(name, value ?: "null")
            }
            request = requestBuilder.build()
        }
        if (!isDebug || builder.level == Level.NONE) {
            return chain.proceed(request)
        }
        val rContentType = request.body?.contentType()
        val rSubtype = rContentType?.subtype
        if (rSubtype != null && (rSubtype.contains("json")
                    || rSubtype.contains("xml")
                    || rSubtype.contains("plain")
                    || rSubtype.contains("html"))
        ) {
            Printer.printJsonRequest(builder, request)
        } else {
            Printer.printFileRequest(builder, request)
        }
        val st = System.nanoTime()
        val response = chain.proceed(request)
        val segment = slashSegments(request.url.encodedPathSegments)
        val chainMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - st)
        val header = response.headers.toString()
        val code = response.code
        val isSuccessful = response.isSuccessful
        val responseBody = response.body
        val contentType = responseBody?.contentType()
        var subtype: String? = null
        if (contentType != null) {
            subtype = contentType.subtype
        }
        val body: ResponseBody = if (subtype != null && (subtype.contains("json")
                    || subtype.contains("xml")
                    || subtype.contains("plain")
                    || subtype.contains("html"))
        ) {
            val bodyString = responseBody?.string()
            val bodyJson = Printer.getJsonString(bodyString)
            Printer.printJsonResponse(
                builder,
                chainMs,
                isSuccessful,
                code,
                header,
                bodyJson,
                segment
            )
            (bodyString ?: "").toResponseBody(contentType)
        } else {
            Printer.printFileResponse(
                builder,
                chainMs,
                isSuccessful,
                code,
                header,
                segment
            )
            return response
        }
        return response.newBuilder().body(body).build()
    }

    class Builder {
        var isDebug = false
        var type: Int = Platform.INFO
            private set
        private var requestTag: String? = null
        private var responseTag: String? = null
        var level = Level.BASIC
            private set
        private val builder: Headers.Builder = Headers.Builder()
        var logger: Logger? = null
            private set

        /**
         * @param level set log level
         * @return Builder
         * @see Level
         */
        fun setLevel(level: Level) = apply { this.level = level }

        val headers: Headers get() = builder.build()

        fun getTag(isRequest: Boolean): String {
            return if (isRequest) requestTag ?: TAG else responseTag ?: TAG
        }

        /**
         * @param name  Filed
         * @param value Value
         * @return Builder
         * Add a field with the specified value
         */
        fun addHeader(name: String?, value: String?) = apply { builder[name!!] = value!! }

        /**
         * Set request and response each log tag
         *
         * @param tag general log tag
         * @return Builder
         */
        fun tag(tag: String) = apply { TAG = tag }

        /**
         * Set request log tag
         *
         * @param tag request log tag
         * @return Builder
         */
        fun request(tag: String?) = apply { requestTag = tag }

        /**
         * Set response log tag
         *
         * @param tag response log tag
         * @return Builder
         */
        fun response(tag: String?) = apply { responseTag = tag }

        /**
         * @param isDebug set can sending log output
         * @return Builder
         */
        fun loggable(isDebug: Boolean) = apply { this.isDebug = isDebug }

        /**
         * @param type set sending log output type
         * @return Builder
         * @see Platform
         */
        fun log(type: Int) = apply { this.type = type }

        /**
         * @param logger manuel logging interface
         * @return Builder
         * @see Logger
         */
        fun logger(logger: Logger?) = apply { this.logger = logger }

        fun build() = LoggingInterceptor(this)

        companion object {
            private var TAG = "okHttp"
        }
    }

    private fun slashSegments(segments: List<String>): String {
        val segmentString = StringBuilder("")
        for (segment in segments) {
            segmentString.append("/").append(segment)
        }
        return segmentString.toString()
    }
}